Skip to content

feat(sync): add the TypeScript and JavaScript adapters - #992

Merged
ctawiah merged 1 commit into
mainfrom
ctawiah/sync-typescript-adapters-3bbf
Nov 9, 2026
Merged

ctawiah merged 1 commit into
mainfrom
ctawiah/sync-typescript-adapters-3bbf

Conversation

@ctawiah

@ctawiah ctawiah commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Context

TypeScript and JavaScript sources need the same static rules as Python, and every format needs one conformance run.

What changes

  • Add the TypeScript and JavaScript adapters. They find framework calls, MCP tools, constants, objects, arrays, and messages, and translate dollar placeholders.
  • Add the package that registers every shipped format. To hold a format back, remove one import.
  • Add the shared test helpers and the conformance tests that run every format through the shared core.

Design notes

  • Template interpolation, spreads, callbacks, and mutated values are read-only.
  • Objects and arrays keep the layout of the text that they replace.

Review focus

  • Do JavaScript and TypeScript share behavior safely?
  • Does the conformance run cover selection, update, reparse, and byte identity for each format?

Verification

  • make build
  • go test ./...
  • golangci-lint run ./...

Related changes

  1. refactor(sync): remove Phase 1 source references #1005: Phase 1 reference removal
  2. feat(sync): add projection and problem core #987: projection and problem core
  3. feat(sync): add the adapter core and the Markdown adapter #988: adapter core and Markdown
  4. feat(sync): add the structured document adapters #989: structured adapters
  5. feat(sync): add the Python adapter #991: Python adapter
  6. This PR: TypeScript and JavaScript adapters
  7. feat(sync): add source descriptor storage #993: descriptor storage
  8. feat(sync): compile and publish repository sources #994: compile and publish
  9. feat(sync): detach descriptor-backed sources #1003: descriptor detach
  10. feat(sync): prepare repository source links #997: link preparation
  11. feat(sync): integrate repository sources with planning and link #995: planning and link
  12. feat(sync): add the link commands #998: link commands

🤖 Generated with Claude Code


Note

Overview
Adds ecmascript adapters for typescript and javascript that statically parse sources with tree-sitter, resolve imports and literals, and expose agents, prompts, tools, and constants for sync—without executing code. Dynamic templates, spreads, mutations, and unresolved imports are marked read-only; dollar placeholders are translated where frameworks require it.

Introduces adaptertest helpers (fixtures, edit application with byte checks, projection utilities) and package all, which blank-imports every shipped format so hosts register them via side effect.

Adds a broad all test suite: format conformance (read → update → reparse), framework matrix coverage for Python and TS/JS, markdown/agent-skill cases, structured-format edge cases, reference validation fuzzing, and new testdata fixtures for frameworks, imports, LangChain, and MCP servers.

Reviewed by Cursor Bugbot for commit f2552df. Bugbot is set up for automated code reviews on this repo. Configure here.

@ctawiah
ctawiah added this pull request to stack #1000 October 30, 2026 20:15
@ctawiah
ctawiah force-pushed the ctawiah/sync-typescript-adapters-3bbf branch from 1358e52 to bea1e3f Compare October 31, 2026 00:05
@ctawiah
ctawiah force-pushed the ctawiah/sync-typescript-adapters-3bbf branch from bea1e3f to 5c0370a Compare November 2, 2026 02:26
@ctawiah
ctawiah force-pushed the ctawiah/sync-typescript-adapters-3bbf branch from 5c0370a to 6312205 Compare November 3, 2026 00:29
@ctawiah ctawiah changed the title feat(sync): add TypeScript and JavaScript adapters feat(sync): add the TypeScript and JavaScript adapters Nov 3, 2026
@ctawiah
ctawiah removed this pull request from stack #1000 November 3, 2026 00:32
@ctawiah
ctawiah added this pull request to stack #1004 November 3, 2026 00:34
@ctawiah
ctawiah removed this pull request from stack #1004 November 3, 2026 00:53
@ctawiah
ctawiah added this pull request to stack #1006 November 3, 2026 00:53
@ctawiah
ctawiah force-pushed the ctawiah/sync-typescript-adapters-3bbf branch from 6312205 to c022ee0 Compare November 4, 2026 19:46
@ctawiah
ctawiah force-pushed the ctawiah/sync-typescript-adapters-3bbf branch 2 times, most recently from 2301e27 to db9ebdd Compare November 4, 2026 22:26
@ctawiah
ctawiah force-pushed the ctawiah/sync-typescript-adapters-3bbf branch from db9ebdd to 33c5104 Compare November 4, 2026 22:52

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

}
}
return nil
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TypeScript export-equals is ignored

Medium Severity

import x = require(...) is bound as a namespace and as the default, but the matching TypeScript export export = value is never read. typeScriptDefaultExport only looks for export default, and typeScriptExportedNames only records export const / export { ... } and module.exports. A CJS file that uses export = TEXT or export = { TEXT } therefore has no importable constants, so a require or import-equals of that module stays unresolved.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ddadedd. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. typeScriptModuleExports reads export = value through typeScriptExportEquals as it reads module.exports = value: an object exports its entries, an identifier or a literal exports the whole module, and a literal declares the default. typeScriptConstants now collects these exports before it handles export default. TestTypeScriptExportEqualsIsImportable covers export = TEXT, export = { TEXT }, and export = "text" through import x = require(...).

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

if previous, seen := instances[id]; seen && previous != kind {
ambiguous[id] = true
}
instances[id] = kind

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Constructor this-assignment drops client kind

Medium Severity

Assigning a constructor parameter onto this, such as this.client = client, records an empty instance kind and can wipe a real client binding. JavaScript has no parameter properties, so the usual constructor pattern never tags this.client, and later this.client.chat.completions.create(...) (and the Anthropic equivalent) is missed. TypeScript only works when the parameter uses an accessibility modifier.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4b811ce. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. When a constructor stores a parameter on this, typeScriptInstances reads the type of the parameter: client: OpenAI tags the property with the client kind, and a parameter without a type records nothing, so it does not wipe the kind that a field gave the property. TestTypeScriptConstructorParametersKeepTheClientKind covers a typed TypeScript parameter and a plain JavaScript parameter over a field.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

}
if frozen != nil {
environment[name], values[name] = frozen, frozen
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Namespace mutation misses named imports

Medium Severity

A write through a namespace import records values.LIST, and the freeze pass only marks that namespace field. A named import of the same export stays writable, and the reverse is also true. At runtime those bindings are one value, so sync can still emit edits for an export that the file already mutated.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9a6741a. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. typeScriptSpreadImportMutations links a named import and the field of a namespace import that hold one export, by the shared value, and spreads a mutation through either name to both before the freeze pass. TestTypeScriptNamedAndNamespaceImportsShareMutations covers a push through the named import, a push through the namespace, and an untouched export that stays writable through both names.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

// scope. The marker counts as a class scope, so a bare name
// does not reach the static fields through it.
entry = append([]typeScriptScope{{name: "static", class: true}}, entry...)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Static field scopes omit static marker

Medium Severity

The static scope marker is applied only to method_definition, so a static field initializer shares ids with an instance field of the same name. Locals in a static field arrow are treated as reassigned, and agents or tools stored on the field get positional ids instead of a stable Class.static.field key.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit c5bc94d. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. typeScriptStaticScope applies the static marker to a static method and to a function or class that initializes a static field, so a static field arrow has the scope Class.static.field and an instance field of the same name is another scope. TestTypeScriptStaticFieldInitializersAreScopesOfTheClass covers a static and an instance field arrow with one name and one local.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

name, value := typeScriptBinding(node)
if name == nil || value == nil {
return
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typed parameter properties skip client kinds

Medium Severity

constructor(private client: OpenAI) never records a client kind. typeScriptBinding returns that parameter with no value, and typeScriptInstances then drops every nil value before typeScriptParameterKind can read the annotation. Methods that call this.client.chat.completions.create(...) or this.client.messages.create(...) are therefore not collected. The covered form is only a parameter property with a new OpenAI() default, which is not the usual TypeScript injection pattern.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 45f1733. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. typeScriptInstances records a parameter property without a value, such as constructor(private client: OpenAI), with the kind that its type names, through typeScriptParameterKind, so this.client.chat.completions.create(...) in a method is collected. TestTypeScriptTypedParameterPropertiesAreClients covers an OpenAI and an Anthropic parameter property next to a string one.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

// default import binds it too.
if visible["default"] == nil && visible["*"] != nil {
visible["default"] = visible["*"]
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CJS object export misses default import

Medium Severity

A default import of a CommonJS module that assigns an object to module.exports (or export = { ... }) stays unresolved. Named keys become exports, but the object itself is never recorded as * or default, so the interop fallback never runs. require and import * as still work; import x from "./mod" and x.field do not, which is the usual esModuleInterop shape.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2480e2e. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. module.exports = { ... } and export = { ... } mark the object as the module, so the import resolution binds the named exports as one object for the * binding and the default import, as require already did. An entry named default replaces the marker. TestTypeScriptDefaultImportOfACommonJSObjectModule covers both forms through import x from and x.field.

return instances[strings.Join(append(typeScriptMemberScopes(node, source), field), ".")]
}
return ""
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Static clients ignored on class name

Medium Severity

A static client field is stored as Class.static.client, and this.client inside a static method finds it. A call through the class name, such as Class.client.chat.completions.create(), never inserts the static marker, so the receiver has no kind and the request is dropped. Constant reads and mutations through that same class name already insert the marker.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2480e2e. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. typeScriptInstanceKind tries the receiver with the static marker after the class name, Bot.static.client for Bot.client, as a constant read through the class name does, so a request through the class name finds the static client inside and outside the class. TestTypeScriptStaticClientsResolveThroughTheClassName covers both.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

return ""
}
return imports.at(use, source)[code.Text(named, source)]
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parameter types miss type-only imports

Medium Severity

typeScriptParameterKind looks up a parameter’s type in the value-import map, which skips import type and { type X } bindings, and it only accepts a bare identifier. Typed parameter properties and this.client = client therefore get no client kind for import type OpenAI from "openai" or client: OpenAI | undefined, so this.client.chat.completions.create(...) is not collected. Those forms are the usual TypeScript shape under verbatimModuleSyntax.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2cc09c6. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. The instances read parameter types through typeScriptTypeImports, which keeps the type-only bindings of import type and { type X } that the value imports leave out, and typeScriptParameterKind finds the one type of the annotation that an import binds, so client: OpenAI | undefined and client?: OpenAI name the kind. TestTypeScriptTypeOnlyImportsNameParameterKinds covers a type-only default import with a union type and a type-only named import stored on this.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

id := typeScriptBindingID(node, code.Text(name, source), source)
declarations = append(declarations, declaration{id: id, value: valueNode})
counts[id]++
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Locals fail to shadow outer constants

Medium Severity

typeScriptConstants only records a variable when it is a simple identifier with an initializer, so destructuring, for/of targets, and uninitialized let never hide an outer constant of the same name. A use of that name then reads the import or module binding instead of the local, and a write can edit the wrong file. Parameters already get a nil shadow, and the import-alias walk already uses bound names for these forms.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4a3d440. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. The declaration walk records a destructuring, an uninitialized let or const, and a for-of or for-in target as bindings without a value, in their block, so a use of the name reads the local instead of an outer constant or import. TestTypeScriptLocalsOfEveryFormHideOuterConstants covers all three forms next to a module use that still resolves.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

if !inClass {
return typeScriptScopes(node, source)
}
return scopes

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anonymous classes break this.client lookup

Medium Severity

typeScriptClassScopes skips a class with no scope name before it can mark inClass, so this.client in a method of an anonymous class is keyed under the method while the field client = new OpenAI() is not. Mixin factories and export default class { ... } then miss the client and drop the request.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 74de14c. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. typeScriptScopeName gives an anonymous class a name of its own from its position, so typeScriptClassScopes marks the class and a field and a method of export default class {} or of a mixin class share one scope, and this.client finds the field. TestTypeScriptAnonymousClassesKeepTheirClients covers a default export class and a mixin factory.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

}
}
return ""
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Class name resolves instance over static

High Severity

typeScriptInstanceKind looks up Bot.client as a bare scoped name before trying Bot.static.client. An instance field uses the id Bot.client, so that hit wins and the static fallback never runs. In JavaScript, Bot.client is the static member; the instance field is this.client. A request through the class name can bind the instance client, or miss the static one, whenever both fields share a name. Constant reads and mutation tracking already try the static marker first.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f38c0d4. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. typeScriptInstanceKind tries the static marker first for a dotted receiver whose root is not this, so Bot.client reads the static field even when an instance field has the same name, and this.client reads the instance field, as a constant read through the class name already did. TestTypeScriptClassNameReadsTheStaticClientFirst covers a class with both fields.

}
}
}
record(typeScriptInstanceID(node, name, source), kind)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Identifier assign wipes client kind

Medium Severity

typeScriptInstances still records an empty kind for an identifier assignment that is not a parameter. That empty kind disagrees with an existing client kind, so record marks the id ambiguous and the cleanup deletes it. this.client = local or client = backup then drops a client that a field or earlier new already established, and later framework calls on that name are skipped. Null, undefined, and untyped parameters already return without recording to avoid this wipe.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f38c0d4. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. An identifier assignment that is not a parameter takes the kind of the assigned name: a name with a kind moves it, and a name without one records nothing, so this.client = kept does not wipe the kind of the field, while two kinds on one name still make it ambiguous. TestTypeScriptIdentifierAssignmentsKeepOrMoveTheClientKind covers a name without a kind on a field and a name with a kind on an uninitialized let.

record(typeScriptInstanceID(node, name, source), kind)
}
}
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typed parameters miss client instances

Medium Severity

A typed parameter such as function run(client: OpenAI) never becomes an instance. typeScriptBinding only returns a parameter that is a parameter property or has a default, and the nil-value branch of typeScriptInstances only reads types for parameter properties. typeScriptParameterKind and type-only imports therefore never tag a normal function or method parameter, so client.chat.completions.create(...) inside that function is not collected.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f38c0d4. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. A required or optional parameter without a default, such as function run(client: OpenAI) or ask(client?: Anthropic), becomes an instance of its function through the type of the parameter, read through the type imports, and a parameter property stays a property of the instance. TestTypeScriptTypedParametersAreClients covers a function parameter with a type-only import and an optional method parameter.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

// The kind of that name decides, and a name without one
// does not change the kind that the property holds.
return
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Locals fail to shadow outer clients

Medium Severity

typeScriptInstances comments that a non-client binding still gets an empty entry so a local shadows an outer client, but several common bindings never record one. An untyped parameter returns without an entry, and an identifier right-hand side with no kind returns to avoid wiping this.client. typeScriptBinding also skips uninitialized lets, destructuring, for-of targets, and JavaScript identifier parameters. A later client.chat.completions.create(...) then resolves to the outer OpenAI instance, so the adapter can collect and edit the wrong call.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9f3673c. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. typeScriptInstances declares an entry without a kind for every binding that has no value: an untyped parameter, a JavaScript identifier or destructured parameter, a catch parameter, a local function or class, an uninitialized let, a destructuring, and a for-of target, and a destructuring or a local bound to a name without a kind declares one too, so a later client.chat.completions.create(...) resolves to the local and not to an outer client. Such an entry is unset: a later assignment gives the name its kind without making it ambiguous, so let client; client = new OpenAI() still works, and this.client = other still keeps the kind of the field. TestTypeScriptLocalsShadowOuterClients covers the five local forms next to a module use that still resolves.

Add the TypeScript and JavaScript adapters with the same static rules as Python. Add the package that registers every shipped format, and the conformance tests that run every format through the shared core.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

There are 16 total unresolved issues (including 14 from previous reviews).

Fix All in Cursor

Reviewed by Cursor Bugbot for commit f2552df. Configure here.

return
}
kind = kinds[code.Text(node, source)]
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Qualified parameter types miss client kinds

Medium Severity

typeScriptParameterKind only matches a single type_identifier or identifier against the import map. Namespace imports bind dotted names such as OpenAI.OpenAI, so a parameter typed as OpenAI.OpenAI or openai.OpenAI never receives a client kind. Constructor parameter properties and typed parameters then stay untracked, and later this.client.chat.completions.create(...) or client.messages.create(...) calls are not collected. Default and named type imports still work; import * as does not.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f2552df. Configure here.

record(typeScriptInstanceID(node, pattern, source), kind)
}
return
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parameter properties skip constructor locals

Medium Severity

A TypeScript parameter property is recorded only as an instance field, then the walk returns without also binding the constructor parameter. this.client in methods works, but a use of the parameter name inside the constructor, such as client.chat.completions.create(...), does not see the client kind. Ordinary function parameters still bind in their function, so constructor parameter properties behave differently from every other typed parameter.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f2552df. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants